package core;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import interfaces.IAnswerManager;
import interfaces.IJobManager;
import interfaces.IManagerProxy;
import interfaces.IStatusManager;
import interfaces.IWorker;
import interfaces.IWorkerManager;;

public class StatusWorkerManager extends Thread implements IStatusManager, IWorkerManager {

	// Tamanho inicial de um job que um worker recem criado irah� receber
	private int initialJobWorkload = Config.INITIAL_JOB_WORKLOAD;

	// Tamanho inicial da carga de trabalho de um trabalhador recem criado
	private int initialWorkerWorkload = Config.INITIAL_WORKER_WORKLOAD;

	// Tamanho inicial da capacidade de trabalho de um trabalhador recem criado
	private float initialWorkerCapacity = Config.INITIAL_WORKER_CAPACITY;

	// Tempo desejado de trabalho
	private long desiredTime = Config.DESIRED_TIME;

	// Define um procurador para o gerente
	private IManagerProxy managerProxy;
	
	// Define um gerente de tarefas
	private IJobManager jobManager;

	// Define um gerente de respostas
	private IAnswerManager answerManager;

	// Historico de estado de cada trabalhador;
	private StatusHistory history;
	
	// Define um trabalhador
	private IWorker worker;

	// Define uma nova rewuisicao de tarefa
	private Request request;

	// Lista de estado de um trabalhador
	BlockingDeque<String> statusList;
	
	// Lista de mapas dos trabalhador
	private Map<String, IWorker> workersList;

	// Lista (mapa) que armazena o historico, por data, das atualizações de estado de um trabalhador
	private Map<String, LinkedList<StatusHistory>> statusHistoryList;
	private LinkedList<StatusHistory> historyList;

	// Define o identificador do gerente
	private String managerId;

	// Define o capacidade do gerente
	private float managerCapacity;

	// Define a carga de trabalho do gerente
	private int managerWorkload;

	// Define o tamanho de trabalho que este gerente
	private int managerWorkSize;
	
	// Identificacao de um worker
	private String workerId;

	// Qauntidade de trabalho de um trabalhador
	private int workerWorkload;

	// Capacidade de trabalho de um trabalhador
	private float workerCapacity;

	// Data de atualizacao do status de um trabalhador
	private long workerCurrentUpdateDate;
	private long workerLastUpdateDate;
	
	private boolean running = true;

	public StatusWorkerManager() {
		try {
			// Instancia uma nova lista bloqueante de estado de um trabalhador
			this.statusList = new LinkedBlockingDeque<String>();
			
			// Instancia uma nova lista sincronizada de trabalhadores
			this.workersList = Collections.synchronizedMap(new LinkedHashMap<String, IWorker>());

			// Instancia uma nova lista (mapa) para armazenar o historico, por data, das atualizacoes de status
			this.statusHistoryList = Collections.synchronizedMap(new LinkedHashMap<String, LinkedList<StatusHistory>>());
			this.historyList = new LinkedList<StatusHistory>();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void run () {
		try {
			while(this.running) {
				try {
					this.workerId = this.statusList.take();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				this.workerCurrentUpdateDate = this.statusHistoryList.get(workerId).get(0).getDate();
				this.workerLastUpdateDate = this.statusHistoryList.get(workerId).get(1).getDate();
				this.workerWorkload = this.statusHistoryList.get(workerId).get(0).getWorkload();
				this.workerCapacity = this.statusHistoryList.get(workerId).get(0).getCapacity();

				// Pega o trab. correspondente na lista de trabalhadores
				this.worker = this.getWorker(workerId);
					
				// Atualiza o status local deste trabalhador
				this.worker.setCapacity(this.workerCapacity);
				this.worker.setWorkload(this.workerWorkload);
				
				// Atualiza o status do gerente
				this.statusUpdate();

				////////////////////////////////////////////////////////////////////////
				//System.out.println("StatusManager (" + this.manager.getID() + ") =>");
				//System.out.println("\t Recebi NODE_STATUS: " + worker.getID());
				//System.out.println("\t\t CAPACIDADE: " + worker.getCapacity());
				//System.out.println("\t\t WORKLOAD: " + worker.getWorkload());
				//System.out.println("\t\t REQUESTSIZE: " + worker.getRequestSize() + "\n");
				// //////////////////////////////////////////////////////////////////////
					
				// Atualiza o status local deste gerente
				//this.manager.statusUpdate();
					
				// //////////////////////////////////////////////////////////////////////
				// System.out.println("StatusManager (" + this.manager.getID() + ") =>");
				// System.out.println("\t Recebi NODE_STATUS: (ANTES)" + worker.getID());
				// System.out.println("\t\t CAPACIDADE: " + worker.getCapacity());
				// System.out.println("\t\t WORKLOAD: " + worker.getWorkload() + "\n");
				// //////////////////////////////////////////////////////////////////////
				this.analyzePossibilityToSendWork(this.workerCapacity, this.workerWorkload, this.workerCurrentUpdateDate, this.workerLastUpdateDate);
				/*if (this.workerWorkload <= (this.desiredTime - (int) (0.5 * this.desiredTime))) { // CHUTE
					
					this.request = new Request();
					this.request.setWorkerId(worker.getID());
					this.request.setRequestSize(worker.getRequestSize());
					
					this.jobManager.newRequest(request);
					
					// //////////////////////////////////////////////////////////////////////
					//System.out.println("StatusManager (" + this.manager.getID() + ") =>");
					//System.out.println("\t Recebi NODE_STATUS: " + worker.getID());
					//System.out.println("\t\t CAPACIDADE: " + worker.getCapacity());
					//System.out.println("\t\t WORKLOAD: " + worker.getWorkload());
					//System.out.println("\t\t REQUESTSIZE: " + worker.getRequestSize() + "\n");
					// //////////////////////////////////////////////////////////////////////
				}*/
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void nodeStatus(String workerId, float capacity, int workload, long updateDate) {
		// TODO Auto-generated method stub
		try {
			
			// //////////////////////////////////////////////////////////////////////
			// System.out.println("StatusWorkerManager (" + this.manager.getID() + ") =>");
			// System.out.println("\t Recebi NODE_STATUS: (ANTES)" + worker.getID());
			// System.out.println("\t\t CAPACIDADE: " + worker.getCapacity());
			// System.out.println("\t\t WORKLOAD: " + worker.getWorkload() + "\n");
			// //////////////////////////////////////////////////////////////////////
			this.history = new StatusHistory(updateDate, workload, capacity);
			this.historyList.addFirst(history);
			this.statusHistoryList.put(workerId, historyList);

			this.statusList.put(workerId);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void analyzePossibilityToSendWork(float capacity, int workload, long currentUpdateDate, long lastUpdateDate) {
		long workingTime = 0;
		long elapsedTime = 0;
		int sizeWork = 0;
		try {
			elapsedTime = (currentUpdateDate - lastUpdateDate);
			workingTime = (long) ((workload-(elapsedTime * capacity)) / capacity);

			// //////////////////////////////////////////////////////////////////////
			//System.out.println("StatusWorkerManager (" + this.getID() + ") =>");
			//System.out.println("\t Recebi NODE_STATUS: " + this.worker.getID());
			//System.out.println("\t\t CAPACIDADE: " + capacity);
			//System.out.println("\t\t WORKLOAD: " + workload);
			//System.out.println("\t\t ELAPSEDTIME: "	+ elapsedTime);
			//System.out.println("\t\t WORKINGTIME: " + workingTime);
			// //////////////////////////////////////////////////////////////////////
			
			if (workingTime < 0) {
				workingTime = 0;
			}
			
			if (workingTime >= 0) {							
				if (workingTime <= (this.desiredTime * 0.5)) {
					sizeWork = (int) ((this.desiredTime * capacity)-workload);
					//System.out.println("\t\t SIZEWORK: "	+ sizeWork);
					if (sizeWork < 0) {
						sizeWork = 0;
					}
					this.request = new Request();
					this.request.setWorkerId(worker.getID());
					this.request.setRequestSize(sizeWork);
					this.jobManager.newRequest(request);
					//System.out.println("\t\t REQUESTSIZE: "	+ sizeWork + "\n");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void newWorker(String workerId, String channelDescription) {
		// TODO Auto-generated method stub
		try {
			// //////////////////////////////////////////////////////////////////////
			//System.out.println("StatusWorkerManager (" + this.getID() + ") =>");
			//System.out.println("\t Recebi NEW_WORKER");
			//System.out.println("\t\t WorkerId: " + workerId);
			//System.out.println("\t\t ChannelDesc: " + channelDescription + "\n");
			// //////////////////////////////////////////////////////////////////////

			Worker worker = new Worker();
			worker.setID(workerId);
			worker.setChannelDesc(channelDescription);
			worker.setCapacity(this.initialWorkerCapacity);
			worker.setWorkload(this.initialWorkerWorkload);
			worker.setRequestSize(this.initialJobWorkload);

			this.history = new StatusHistory(0, worker.getWorkload(), worker.getCapacity());
			this.historyList.addFirst(history);
			this.statusHistoryList.put(workerId, historyList);
			
			this.managerProxy.workerConnect(workerId, channelDescription);
			this.managerProxy.bossContactSend();
			
			this.request = new Request();
			this.request.setWorkerId(worker.getID());
			this.request.setRequestSize(worker.getRequestSize());
			
			this.setWorker(workerId, worker);
			this.jobManager.newRequest(request);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public synchronized void setWorker(String workerId, IWorker worker) {
		// TODO Auto-generated method stub
		try {
			this.workersList.put(workerId, worker);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public synchronized IWorker getWorker(String workerId) {
		// TODO Auto-generated method stub
		try {
			return this.workersList.get(workerId);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	@Override
	public void setID(String id) {
		// TODO Auto-generated method stub
		this.managerId = id;
	}

	@Override
	public String getID() {
		// TODO Auto-generated method stub
		return this.managerId;
	}

	@Override
	public void setCapacity(float capacity) {
		// TODO Auto-generated method stub
		this.managerCapacity = capacity;
	}

	@Override
	public float getCapacity() {
		// TODO Auto-generated method stub
		return this.managerCapacity;
	}

	@Override
	public void setWorkload(int workload) {
		// TODO Auto-generated method stub
		this.managerWorkload = workload;
	}

	@Override
	public int getWorkload() {
		// TODO Auto-generated method stub
		return this.managerWorkload;
	}

	@Override
	public void setRequestSize(int requestSize) {
		// TODO Auto-generated method stub
		this.managerWorkSize = requestSize;
	}

	@Override
	public int getRequestSize() {
		// TODO Auto-generated method stub
		return this.managerWorkSize;
	}
	
	@Override
    public synchronized void statusUpdate() {
            int capacityTotal = 0;
            int workloadTotal = 0;

            Iterator<String> i = this.workersList.keySet().iterator();
            while (i.hasNext()) {
                    String key = (String) i.next();
                    capacityTotal += this.workersList.get(key).getCapacity();
                    workloadTotal += this.workersList.get(key).getWorkload();
                    this.setCapacity(capacityTotal);
                    this.setWorkload(workloadTotal);
            }
    }
	
	@Override
	public synchronized void reduceManagerWorkload(int workload) {
		try {
			this.managerWorkload -= workload;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public synchronized  void increaseManagerWorkload(int workload) {
		try {
			this.managerWorkload += workload;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public IManagerProxy getManagerProxy() {
		// TODO Auto-generated method stub
		return this.managerProxy;
	}

	@Override
	public void setManagerProxy(IManagerProxy managerProxy) {
		// TODO Auto-generated method stub
		this.managerProxy = managerProxy;
	}

	@Override
	public IJobManager getJobManager() {
		// TODO Auto-generated method stub
		return this.jobManager;
	}

	@Override
	public void setJobManager(IJobManager jobManager) {
		// TODO Auto-generated method stub
		this.jobManager = jobManager;
	}

	@Override
	public IAnswerManager getAnswerManager() {
		// TODO Auto-generated method stub
		return this.answerManager;
	}

	@Override
	public void setAnswerMManager(IAnswerManager answerManager) {
		// TODO Auto-generated method stub
		this.answerManager = answerManager;
	}
}
